using System;
using System.Collections.Generic;
using Heaven.Graphics;
using Heaven.Mathematics;
using Heaven.Texturing;

namespace Heaven.Engine
{
    /// <summary>
    /// This is property of an object.
    /// The particular implementation can be designed 
    /// for shaders or scene objects parameters
    /// </summary>
    public class Property<T> : IProperty
    {
        // The value
        volatile object value;

        // Weak event
        WeakMulticastDelegate invalidated = new WeakMulticastDelegate();

        /// <summary>
        /// Occurs when the value has been changed
        /// </summary>
        public event EventHandler Invalidated
        {
            add { invalidated.Add(value); }
            remove { invalidated.Remove(value); }
        }

        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        public T Value
        {
            get { return (T)value; }
            set
            {
                // Skip if values are equal
                if ((this.value != null) && (this.value.Equals(value))) return;

                this.value = value;
                invalidated.Invoke(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        object IProperty.Value
        {
            get { return value; }
            set
            {
                // Skip if values are equal
                if ((this.value != null) && (this.value.Equals(value))) return;

                this.value = value;
                invalidated.Invoke(this, EventArgs.Empty);
            }
        }
        
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Property(T value)
        {
            this.value = value;
        }
    }

    /// <summary>
    /// This is an interface of an property.
    /// The particular implementation can be designed 
    /// for shaders or scene objects parameters
    /// </summary>
    public interface IProperty
    {
        /// <summary>
        /// Occurs when the value has been changed
        /// </summary>
        event EventHandler Invalidated;

        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        object Value { get; set; }
    }



    /*
    /// <summary>
    /// This is property of an object.
    /// The particular implementation can be designed 
    /// for shaders or scene objects parameters
    /// </summary>
    public class Property
    {
        /// <summary>
        /// Occurs when the value has been changed
        /// </summary>
        public event PropertyChangedEventHandler ValueChanged;

        // Current value
        volatile object value;

        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        public object Value
        {
            get { return value; }
            set
            {
                // Skip if values are equal
                if ((this.value != null) && (this.value.Equals(value))) return;

                object previous = this.value;
                this.value = value;
                if (ValueChanged != null) ValueChanged(this, new PropertyChangedEventAgs(previous, value));
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Initial value</param>
        public Property(object value)
        {
            this.value = value;
        }
    }


    #region Property Changed Event

    /// <summary>
    /// This class contains event data 
    /// relative to changing a property
    /// </summary>
    public class PropertyChangedEventAgs : EventArgs
    {        
        /// <summary>
        /// The previous value
        /// </summary>
        public readonly object PreviousValue;

        /// <summary>
        /// The new value
        /// </summary>
        public readonly object CurrentValue;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="oldValue">The previous value</param>
        /// <param name="newValue">The new value</param>
        public PropertyChangedEventAgs(object oldValue, object newValue)
        {
            PreviousValue = oldValue;
            CurrentValue = newValue;
        }
    }

    /// <summary>
    /// This is delegate for event in which a property changed
    /// </summary>
    /// <param name="sender">A sender</param>
    /// <param name="args">An argument of event</param>
    public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventAgs args);
    
    #endregion

    #region Generic Implementation

    /// <summary>
    /// This is a generic class of a controller 
    /// which incapsulates interaction with a 
    /// parameter of scene's entity
    /// </summary>
    public class GenericProperty<T> : Property
    {
        #region Events

        /// <summary>
        /// Occurs when the value has been changed
        /// </summary>
        public event PropertyChangedEventHandler ValueChanged;

        #endregion

        #region Fields

        // Current value
        T value;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        public T Value
        {
            get { return value; }
            set
            {
                // Skip if values are equal
                if ((this.value != null) && (this.value.Equals(value))) return;

                object previous = this.value;
                this.value = value;
                if (ValueChanged != null) ValueChanged(this, new PropertyChangedEventAgs(previous, value));
            }
        }


        /// <summary>
        /// Gets or sets the current value of 
        /// the object, it allows to perform the 
        /// animation using this property
        /// </summary>
        object Property.Value
        {
            get { return value; }
            set
            {
                // Skip if values are equal
                if (this.value.Equals(value)) return;

                object previous = this.value;
                this.value = (T)value;
                if (ValueChanged != null) ValueChanged(this, new PropertyChangedEventAgs(previous, value));
            }
        }

        #endregion
    }

    #endregion

    #region Color Property

    /// <summary>
    /// Represent color property
    /// </summary>
    public class ColorProperty : GenericProperty<Color>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public ColorProperty() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public ColorProperty(Color value)
        {
            Value = value;
        }
    }

    #endregion

    #region Matrix3 Property

    /// <summary>
    /// Represent Matrix3 property
    /// </summary>
    public class Matrix3Property : GenericProperty<Matrix3>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public Matrix3Property() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Matrix3Property(Matrix3 value)
        {
            Value = value;
        }
    }

    #endregion

    #region Matrix4 Property

    /// <summary>
    /// Represent Matrix4 property
    /// </summary>
    public class Matrix4Property : GenericProperty<Matrix4>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public Matrix4Property() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Matrix4Property(Matrix4 value)
        {
            Value = value;
        }
    }

    #endregion

    #region Scalar Property

    /// <summary>
    /// Represent scalar value property
    /// </summary>
    public class ScalarProperty : GenericProperty<double>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public ScalarProperty() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public ScalarProperty(Double value)
        {
            Value = value;
        }
    }

    #endregion

    #region Texture Property

    /// <summary>
    /// Represent texture property
    /// </summary>
    public class TextureProperty : GenericProperty<Texture>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public TextureProperty() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public TextureProperty(Texture value)
        {
            Value = value;
        }
    }

    #endregion

    #region Vector2 Property

    /// <summary>
    /// Represent Vector2 property
    /// </summary>
    public class Vector2Property : GenericProperty<Vector2>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public Vector2Property() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Vector2Property(Vector2 value)
        {
            Value = value;
        }
    }

    #endregion

    #region Vector3 Property

    /// <summary>
    /// Represent Vector3 property
    /// </summary>
    public class Vector3Property : GenericProperty<Vector3>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public Vector3Property() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Vector3Property(Vector3 value)
        {
            Value = value;
        }
    }

    #endregion

    #region Vector4 Property

    /// <summary>
    /// Represent Vector4 property
    /// </summary>
    public class Vector4Property : GenericProperty<Vector4>
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        public Vector4Property() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="value">Value</param>
        public Vector4Property(Vector4 value)
        {
            Value = value;
        }
    }

    #endregion*/
}
